/*!
 * \file gico_object.h
 *
 */

#ifndef GICO_OBJECT_H_

/* FIXME: Use only the includes that are needed */
#include <gsl/gsl_errno.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_complex_math.h>

#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>

/*!
 * \brief Macro to check if a gico_object has been initialized or not
 */
#define GICO_OBJECT_CHECK_INIT(obj) ((obj != NULL) && (obj->data != NULL))

/*!
 * \brief Enumeration of the types of gico_objects
 */
enum gico_object_kind
{
	GICO_REAL = 1,			/* 00001 : real object */
	GICO_IMAG = 2,			/* 00010 : imaginary object */
	GICO_COMPLEX = 3,		/* 00011 : complex object */
	GICO_ABS = 4,			/* 00100 : modulus object (|x|) */
	GICO_ABS2 = 8,			/* 01000 : squared modulus object (|x|^2) */
	GICO_ANGLE = 16			/* 10000 : angle object (arg(x)) */
};

/*!
 * \brief Enumeration of the alignments of the vectors
 */
enum gico_vector_align
{
	GICO_HOR = 1,			/* horizontal alignment */
	GICO_VER = 2			/* vertical alignment */
};

/*!
 *
 */
typedef struct {
	size_t N[2];
	double d[2];
	enum gico_object_kind kind;
	double *data;
} gico_object;

/*!
 * \brief Allocates the memory for a gico_object.
 *
 * \param newone gico_object pointer to pointer in which allocate the memory
 * \param N1 Size of the gico_object in the first coordinate
 * \param N2 Size of the gico_object in the second coordinate
 * \param kind gico_object_kind with the type of gico_object
 *
 * \return Returning code of the function
 *
 * This function allocates a segment in memory in which to store the gico_object that has N1 rows
 * and N2 columns and is of the type specified by kind.
 */
int gico_object_alloc(gico_object **newone, size_t N1, size_t N2, enum gico_object_kind kind);

/*!
 * \brief Deallocates the memory of a gico_object.
 *
 * \param oldone gico_object pointer to deallocate
 *
 * \return Returning code of the function
 *
 * This function deallocates the segment of the memory assigned to oldone by a previous call of
 * gico_object_alloc().
 */
int gico_object_dealloc(gico_object *oldone);

/*!
 * \brief Sets the value in a certain position of a gico_object.
 *
 * \param obj gico_object to operate
 * \param n1 Row in which insert the value
 * \param n2 Column in which insert the value
 * \param value Numerical value to insert
 *
 * \return Returning code of the function
 *
 * This function sets the double number in the position (n1, n2) of the gico_object pointed by obj
 * to value.
 */
int gico_object_set_value(gico_object *obj, size_t n1, size_t n2, double value);

/*!
 * \brief Gets the value in a certain position of a gico_object.
 *
 * \param obj gico_object to operate
 * \param n1 Row in which get the value
 * \param n2 Column in which get the value
 * \param value Pointer to the memory where to store the value
 *
 * \return Returning code of the function
 *
 * This function gets the numerical double in position (n1, n2) of the gico_object obj and stores it
 * in the memory pointed by value.
 */
int gico_object_get_value(gico_object *obj, size_t n1, size_t n2, double *value);

/*!
 * \brief Sets the value in a certain position of a gico_object.
 *
 * \param obj gico_object to operate
 * \param n1 Row in which insert the value
 * \param n2 Column in which insert the value
 * \param value Numerical value to insert
 *
 * \return Returning code of the function
 *
 * This function sets the complex number in the position (n1, n2) of the gico_object pointed by obj
 * to value.
 */
int gico_object_set_cvalue(gico_object *obj, size_t n1, size_t n2, gsl_complex value);

/*!
 * \brief Gets the complex value in a certain position of a gico_object.
 *
 * \param obj gico_object to operate
 * \param n1 Row in which get the value
 * \param n2 Column in which get the value
 * \param value Pointer to the memory where to store the complex value
 *
 * \return Returning code of the function
 *
 * This function gets the numerical complex in position (n1, n2) of the gico_object obj and stores
 * it in the memory pointed by value.
 */
int gico_object_get_cvalue(gico_object *obj, size_t n1, size_t n2, gsl_complex *value);

/*!
 * \brief Sets a vector of doubles in a certain position of a gico_object.
 *
 * \param obj gico_object to operate
 * \param n1 Row in which insert the value
 * \param n2 Column in which insert the value
 * \param va Orientation of the vector (column or row)
 * \param vector Pointer to the memory where the gsl_vector is stored
 *
 * \return Returning code of the function
 *
 * This function sets the positions of the gico_object that coincide with the vector that starts in
 * position (n1, n2) and has alignment va, with the values of the gsl_vector vector.
 */
int gico_object_set_vector(gico_object *obj, size_t n1, size_t n2, enum gico_vector_align va, gsl_vector *vector);

/*!
 * \brief Gets the vector of doubles that is stored in a certain position of a gico_object.
 *
 * \param obj gico_object to operate
 * \param n1 Row in which get the value
 * \param n2 Column in which get the value
 * \param va Orientation of the vector (column or row)
 * \param vector Pointer to the memory where the gsl_vector has to be stored
 *
 * \return Returning code of the function
 *
 * This function gets the positions of the gico_object that coincide with the vector that starts in
 * position (n1, n2) and has alignment va, and stores them in the gsl_vector vector.
 */
int gico_object_get_vector(gico_object *obj, size_t n1, size_t n2, enum gico_vector_align va, gsl_vector *vector);

/*!
 * \brief Sets a vector of complexes in a certain position of a gico_object.
 *
 * \param obj gico_object to operate
 * \param n1 Row in which insert the value
 * \param n2 Column in which insert the value
 * \param va Orientation of the vector (column or row)
 * \param vector Pointer to the memory where the gsl_vector_complex is stored
 *
 * \return Returning code of the function
 *
 * This function sets the positions of the gico_object that coincide with the vector that starts in
 * position (n1, n2) and has alignment va, with the values of the gsl_vector_complex vector.
 */
int gico_object_set_cvector(gico_object *obj, size_t n1, size_t n2, enum gico_vector_align va, gsl_vector_complex *vector);

/*!
 * \brief Gets the vector of complexes that is stored in a certain position of a gico_object.
 *
 * \param obj gico_object to operate
 * \param n1 Row in which get the value
 * \param n2 Column in which get the value
 * \param va Orientation of the vector (column or row)
 * \param vector Pointer to the memory where the gsl_vector_complex has to be stored
 *
 * \return Returning code of the function
 *
 * This function gets the positions of the gico_object that coincide with the vector that starts in
 * position (n1, n2) and has alignment va, and stores them in the gsl_vector_complex vector.
 */
int gico_object_get_cvector(gico_object *obj, size_t n1, size_t n2, enum gico_vector_align va, gsl_vector_complex *vector);

/*!
 * \brief Sets the matrix of doubles in a certain position of a gico_object.
 *
 * \param obj gico_object to operate
 * \param n1 Row in which insert the value
 * \param n2 Column in which insert the value
 * \param matrix Pointer to the memory where the gsl_matrix is stored
 *
 * \return Returning code of the function
 *
 * This function sets the positions of the gico_object that coincide with the matrix that starts in
 * position (n1, n2) with the values of the gsl_matrix matrix.
 */
int gico_object_set_matrix(gico_object *obj, size_t n1, size_t n2, gsl_matrix *matrix);

/*!
 * \brief Gets the matrix of doubles that is stored in a certain position of a gico_object.
 *
 * \param obj gico_object to operate
 * \param n1 Row in which insert the value
 * \param n2 Column in which insert the value
 * \param matrix Pointer to the memory where the gsl_matrix is stored
 *
 * \return Returning code of the function
 *
 * This function gets the positions of the gico_object that coincide with the matrix that starts in
 * position (n1, n2) and stores them in the gsl_matrix matrix.
 */
int gico_object_get_matrix(gico_object *obj, size_t n1, size_t n2, gsl_matrix *matrix);

/*!
 * \brief Sets the matrix of complexes in a certain position of a gico_object.
 *
 * \param obj gico_object to operate
 * \param n1 Row in which insert the value
 * \param n2 Column in which insert the value
 * \param matrix Pointer to the memory where the gsl_matrix is stored
 *
 * \return Returning code of the function
 *
 * This function sets the positions of the gico_object that coincide with the matrix that starts in
 * position (n1, n2) with the values of the gsl_matrix_complex matrix.
 */
int gico_object_set_cmatrix(gico_object *obj, size_t n1, size_t n2, gsl_matrix_complex *matrix);

/*!
 * \brief Gets the matrix of complexes that is stored in a certain position of a gico_object.
 *
 * \param obj gico_object to operate
 * \param n1 Row in which insert the value
 * \param n2 Column in which insert the value
 * \param matrix Pointer to the memory where the gsl_matrix is stored
 *
 * \return Returning code of the function
 *
 * This function gets the positions of the gico_object that coincide with the matrix that starts in
 * position (n1, n2) and stores them in the gsl_matrix_complex matrix.
 */
int gico_object_get_cmatrix(gico_object *obj, size_t n1, size_t n2, gsl_matrix_complex *matrix);

/*!
 * \brief Saves a gico_object in a file.
 *
 * \param obj gico_object to operate
 * \param filename File name (including path) to store the gico_object
 *
 * \return Returning code of the function
 *
 * This function dumps the information into a binary file for its further use. It can (and has to)
 * be loaded by the function gico_object_load.
 *
 * The use of this file for graphical representation with gnuplot is discouraged, as there is a
 * function that specifically saves the gico_object for this purpose: gico_object_save_gnuplot().
 */
int gico_object_save(gico_object *obj, const char filename[]);

/*!
 * \brief Loads the gico_object stored in a file.
 *
 * \param obj gico_object to operate
 * \param filename File name (including path) to load the gico_object from
 *
 * \return Returning code of the function
 *
 * This function retrieves the information of a gico_object from a binary file. The file has to be
 * previously generated by the function gico_object_save.
 *
 * This function is incompatible with the binary files generated by gico_object_save_gnuplot(),
 * which are only meant to be used with gnuplot.
 */
int gico_object_load(const char filename[], gico_object **obj);

/*!
 * \brief Saves a gico_object in a binary file for its representation in Gnuplot.
 *
 * \param obj gico_object to operate
 * \param filename File name (including path) to store the binary file
 *
 * \return Returning code of the function
 *
 * This function dumps the information of the gico_object into a binary file for its further
 * representation with Gnuplot. The format of the binary file is a succession of single precision
 * floats like:
 *
 *     <N+1>    <x0>     <x1>  ...  <xN>
 *     <y0>    <A0,0>   <A0,1> ... <A0,N>
 *     <y1>    <A1,0>   <A1,1> ... <A1,N>
 *      .         .        .          .
 *      .         .        .          .
 *      .         .        .          .
 *     <yM>    <AM,0>   <AM,1> ... <AM,N>
 *
 * where N+1 is the number of points in direction X (number of columns), M+1 is the number of points
 * in direction Y (number of rows), <xi> is the i-th value of the X axis, <yj> the j-th value of the
 * Y axis, and <Ai,j> the element (i,j) of the matrix to be saved.
 */
int gico_object_save_gnuplot(gico_object *obj, enum gico_object_kind kind, const char filename[]);

/*!
 * \brief Primitive to write blocks of data to files.
 *
 * \param fd File descriptor where to perform the write action
 * \param buffer Array that is to be dumped into the file
 * \param n_bytes Number of bytes to be dumped from the buffer array
 *
 * \return Returning code of the function
 *
 * This function is used internally in the gico_object_save() function. It dumps into the file whose
 * file descriptor is fd, n_bytes bytes from the array buffer.
 *
 * \warning The personal use of this function is discouraged, as it exists as an internal tool only.
 */
int gico_write(int fd, const void *buffer, size_t n_bytes);

/*!
 * \brief Primitive to read blocks of data from files.
 *
 * \param fd File descriptor where to perform the read action
 * \param buffer Array where to store the read data
 * \param n_bytes Number of bytes to be read from the file
 *
 * \return Returning code of the function
 *
 * This function is used internally in the gico_object_load() function. It reads n_bytes bytes from
 * the file whose file descriptor is fd and store them in the array buffer.
 *
 * \warning The personal use of this function is discouraged, as it exists as an internal tool only.
 */
int gico_read(int fd, void *buffer, size_t n_bytes);

#define GICO_OBJECT_H_

#endif /* GICO_OBJECT_H_ */
